--[[
  机器人管理器
 ]]

local skynet = require "skynet"
local cluster = require "skynet.cluster"
local filename = "robotmgr.lua"
local filelog = require "filelog"
local configdao = require "configdao"
local servicepoolmng = require "incrservicepoolmng"
require "skynet.manager"

local json = require "cjson"
json.encode_sparse_array(true,1,1)

require "pubenum"

--初始化随机数
math.randomseed( os.time())
math.random()

--心跳间隔，7s
local heart_interval_time = 7

--登录login的key
local platform2login_key = ""

--------------------------------

local RobotMgr = {
    CMD = {},
    handler = {},
    agentpool = nil,
    robotpool = nil,
    address_infos = nil,
    is_saverobot = true,  --是否保存机器人
    robot_data = {
        cur_robot_num = 0,--当前机器人数
        robot_list = {},  --机器人列表
        conf = {},        --机器人配置
    }
}

-------------------

--生成机器人用户信息
function RobotMgr.handler.gen_robotinfo()
    if not RobotMgr.robotpool or #(RobotMgr.robotpool) < 1 then
        return nil
    end
    local len = #(RobotMgr.robotpool)
    local idx = math.random(1,len)
    local robotinfo = RobotMgr.robotpool[idx]
    RobotMgr.robotpool[idx] = RobotMgr.robotpool[len]
    RobotMgr.robotpool[len] = nil

    return robotinfo
end

--生成机器人地址信息
function RobotMgr.handler.gen_address_info()
    local address_info = {}

    --随机机器人ip,gps
    local ipstr = "172."
    for i = 2,4 do
        if i == 2 then
            if math.random(1,2) == 1 then
                ipstr = ipstr .. "16"
            else
                ipstr = ipstr .. "31"
            end
        else
            local r = math.random(1,254) or 39
            ipstr = ipstr .. tostring(r)
        end
        if i < 4 then
            ipstr = ipstr .. "."
        end
    end
    address_info.ip_addr = ipstr

    local gpsaddr = { state = 2}
    gpsaddr.latitude = math.random(10000,1000000) / 10000
    gpsaddr.longitude = math.random(10000,1000000) / 10000
    if RobotMgr.address_infos then
        local r1 = math.random(1,#(RobotMgr.address_infos))
        if RobotMgr.address_infos[r1] then
            local r2 = math.random(1,#(RobotMgr.address_infos[r1].city_list))
            local province = RobotMgr.address_infos[r1].province
            local city = RobotMgr.address_infos[r1].city_list[r2]
            if province ~= city then
                gpsaddr.address = tostring(province) .. city
            else
                gpsaddr.address = tostring(province)
            end
        end
    end
    address_info.gps_addr = json.encode(gpsaddr)

    return address_info
end

--载入机器人地址信息
function RobotMgr.handler.load_robot_addrinfo()
    local addrinfo_str = ""
    local fp = io.open("province.json")
	if fp then
	    addrinfo_str = fp:read("*all")
        fp:close()
	end

    local address_infos = {}
    local addrinfo = json.decode(addrinfo_str)
    if type(addrinfo) == "table" then
        for k,v in pairs(addrinfo) do
            local item = {province = k, city_list = v}
            table.insert(address_infos, item)
        end
    end
    RobotMgr.address_infos = address_infos
end

--创建一个机器人
function RobotMgr.handler.cretae_robot(config)
    local robotinfo = RobotMgr.handler.gen_robotinfo()
    if not robotinfo then
        filelog.sys_error("RobotMgr.handler.cretae_robot not enough robot")
        return false
    end
    
    local rid = robotinfo.rid
    local agentservice = RobotMgr.agentpool:create_service()
    if agentservice == nil then
		filelog.sys_error("RobotMgr.handler.cretae_robot not enough agentservice")		
		return false
	end

    local robotmoney = {
        diamond = config.diamond or 10000,
        coin = config.coin or 0,
    }

    local robotconf = RobotMgr.robot_data.conf
    local status, result = skynet.pcall(skynet.call, agentservice.service, "lua", "cmd","create_session", {
        robotinfo = robotinfo,
        robotmoney = robotmoney,
        heart_interval_time = heart_interval_time,
        loginsvr_conf = robotconf.loginsvr_conf,
        gatesvr_conf = robotconf.gatesvr_conf,
        gamesvr_conf = robotconf.gamesvr_conf,
        platform2login_key = platform2login_key,
        logicname = config.logicname,
    })

    if (not status) or (not result) then
		filelog.sys_error("RobotMgr.handler.cretae_robot agent create_session failed", result)
		skynet.pcall(skynet.kill, agentservice.service) 
		return false		
	end

    RobotMgr.robot_data.robot_list[rid] = {
        rid = rid,
        agent = agentservice.service,
        robotinfo = robotinfo,
        gamename = config.logicname,
    }
    RobotMgr.robot_data.cur_robot_num = RobotMgr.robot_data.cur_robot_num + 1

    return rid
end


--机器人进入游戏（进入大厅）
function RobotMgr.handler.robot_entergame(robj)
    return skynet.call(robj.agent, "lua", "cmd", "robot_entergame")
end

--机器人进桌
function RobotMgr.handler.robot_entertable(robj, gamename, table_index, schedule_id)
    local status,result = skynet.pcall(skynet.call, robj.agent, "lua", "cmd", "robot_entertable", gamename, table_index, schedule_id)
    if status and result then
        return result
    end
    return 0
end

--机器人进入匹配
function RobotMgr.handler.robot_entermatch(robj, gamename, match_type, match_level, schedule_id)
    local status,result = skynet.pcall(skynet.call, robj.agent, "lua", "cmd", "robot_entermatch", gamename, match_type, match_level, schedule_id)
    if status and result then
        return result
    end
    return 0
end

--机器人离桌
function RobotMgr.handler.robot_leavetable(robj, gamename, table_index, schedule_id)
    local status,result = skynet.pcall(skynet.call, robj.agent, "lua", "cmd", "delay_leavetable", nil, gamename, table_index, schedule_id)
    if status and result then
        return result
    end
    return 0
end

--检测机器人是否可离桌
function RobotMgr.handler.check_leavetable(robj)
    return skynet.call(robj.agent, "lua", "cmd", "check_leavetable")
end

--清空数据
function RobotMgr.handler.clear()
	for id,obj in pairs(RobotMgr.robot_data.robot_list) do
		 skynet.pcall(skynet.send, obj.agent, "lua", "cmd", "close")
         RobotMgr.robot_data.robot_list[id] = nil
	end

    if RobotMgr.agentpool ~= nil then
		local iter = RobotMgr.agentpool:idle_service_iter()
		local service = iter()
		while service do
			skynet.pcall(skynet.send, service, "lua", "cmd", "close")
			service = iter()
		end
	end
	
    RobotMgr.robot_data.cur_robot_num = 0
    RobotMgr.robotpool = nil
end

--保存机器人钱
function RobotMgr.handler.save_money(robotpool)
    if not robotpool then
        return
    end

    --将机器人身上钱放回池子
    for _,robj in pairs(robotpool) do
        local rid = robj.rid or 0
        local status,resul_data = skynet.pcall(cluster.call, "datanode", ".router", "select_rk", rid, "rs_money", true)
        if status and resul_data and resul_data[1] ~= 0 and resul_data[2] then
            local moneydata = resul_data[2][rid]
            if moneydata.diamond > 0 then
                skynet.pcall(skynet.call, ".inventorymgr", "lua", "save_money", rid, "diamond", moneydata.diamond, moneydata.diamond, nil)
            end
        end
    end
    
end

---------------------------------------------
--外部接口

--
-- 初始化agent池
--
function RobotMgr.CMD.init(nodeconf)
    RobotMgr.agentpool = servicepoolmng:new({}, {service_name = "agent", service_size = nodeconf.agentsize, incr = nodeconf.agentincr}, "websocketnetpack")

    local robotconf = RobotMgr.robot_data.conf or {}
    local loginconf = configdao.get_cfgsvrs_key("loginnode")
    local gateconf = configdao.get_cfgsvrs_key("gatenode")
    robotconf.gatesvr_conf = { ip = "127.0.0.1", port = gateconf.svr_port }
    robotconf.loginsvr_conf = { ip = "127.0.0.1", port = loginconf.svr_port }
    robotconf.maxrobot = nodeconf.maxrobot or 10000

    platform2login_key = configdao.get_cfgsvrs_key("platform2login_key")

    --加载机器人城市信息
    RobotMgr.handler.load_robot_addrinfo()

    --加载机器人库
    local sqlstr = "select * from rs_robot where rid != 99 and rid != 100 order by rid limit " .. tostring(robotconf.maxrobot)
    local status,resul_data = skynet.pcall(cluster.call, "datanode", ".router", "mysqlquery", 1, sqlstr)
    if status and resul_data and resul_data[1] ~= 0 and resul_data[2] then
        RobotMgr.robotpool = resul_data[2]
        --获取rs_info信息
        for _,robotinfo in pairs(RobotMgr.robotpool) do
            local status,result = skynet.pcall(cluster.call, "datanode", ".router", "select_rk", robotinfo.rid, "rs_info", true)
            if status and result and result[1] ~= 0 and result[2] then
                local info = result[2][robotinfo.rid] or {}
                robotinfo.sex = info.sex
                robotinfo.logo = info.logo
                robotinfo.rolename = info.rolename
                robotinfo.logoframe = info.logoframe
            end
            --生成机器人ip和gps信息
            local addrinfo = RobotMgr.handler.gen_address_info()
            robotinfo.gps_addr = addrinfo.gps_addr
            robotinfo.ip_addr = addrinfo.ip_addr
        end
    end

    --机器人不足，停止机器人调度
    if not RobotMgr.robotpool or #RobotMgr.robotpool < 30 then
        skynet.error("not enough robot,stop scheduler run!")
        skynet.send(".schedulemgr", "lua", "change_robot_state", false)
    end

    --将机器人身上钱放回池子
    RobotMgr.handler.save_money(RobotMgr.robotpool)

    return true
end

--获得机器人数目
function RobotMgr.CMD.get_robot_num()
    return (RobotMgr.robot_data.cur_robot_num or 0)
end

--
-- 申请N个机器人
-- num 机器人数量
-- conf 该批机器人的一些配置
function RobotMgr.CMD.apply_robots(num, conf)
    local robotlist = {}
    local n = 2 * num
    for i = 1,n do
        local rid = RobotMgr.handler.cretae_robot(conf)
        if rid then
            table.insert(robotlist,rid)
            if #robotlist == num then
                break
            end
        end
    end
    
    return robotlist
end

--添加机器人到桌子
function RobotMgr.CMD.do_robot_entertable(gamename, table_index, robotlist, schedule_id)
    local enternum = 0
    for _,rid in pairs(robotlist) do
        local robj = RobotMgr.robot_data.robot_list[rid]
        if robj then
            local num = RobotMgr.handler.robot_entertable(robj,gamename,table_index,schedule_id)
            enternum = enternum + (tonumber(num) or 0)
        end
    end
    
    return enternum
end

--让机器人离桌
function RobotMgr.CMD.do_robot_leavetable(gamename, table_index, robotlist, schedule_id)
    local leavenum = 0
    for _,rid in pairs(robotlist) do
        local robj = RobotMgr.robot_data.robot_list[rid]
        if robj then
            local num = RobotMgr.handler.robot_leavetable(robj,gamename,table_index,schedule_id)
            leavenum = leavenum + (tonumber(num) or 0)
        end
    end
    
    return leavenum
end

--让机器人进入匹配
function RobotMgr.CMD.do_robot_match(gamename, match_type, match_level, robotlist, schedule_id)
    local enternum = 0
    for _,rid in pairs(robotlist) do
        local robj = RobotMgr.robot_data.robot_list[rid]
        if robj then
            local num = RobotMgr.handler.robot_entermatch(robj,gamename,match_type,match_level,schedule_id)
            enternum = enternum + (tonumber(num) or 0)
        end
    end
    
    return enternum
end

--删除一个机器人
function RobotMgr.CMD.delete_robot(rid) 
    local robot_data = RobotMgr.robot_data
    if robot_data.robot_list[rid] then
        local robotinfo = robot_data.robot_list[rid].robotinfo
        table.insert(RobotMgr.robotpool,robotinfo)
        robot_data.robot_list[rid] = nil
        robot_data.cur_robot_num = robot_data.cur_robot_num - 1
    end
end

--移除所有机器人
--只是将机器人全踢桌
function RobotMgr.CMD.remove_allrobots(gamename)
    for id,obj in pairs(RobotMgr.robot_data.robot_list) do
        if not gamename or obj.gamename == gamename then
            skynet.pcall(skynet.send, obj.agent, "lua", "cmd", "close", true)
        end
	end
end

--检测机器人是否可以离桌
--可以离桌会自动离桌
function RobotMgr.CMD.check_leavetable(rid)
    rid = tonumber(rid) or 0
    local robj = RobotMgr.robot_data.robot_list[rid]
    if robj then
        return RobotMgr.handler.check_leavetable(robj)
    end
end

--获得机器人info信息
--返回机器人信息列表
--num 返回机器人info个数,默认1个
function RobotMgr.CMD.gen_robotinfo(num)
    num = tonumber(num) or 1

    local robot_infos = {}
    for i = 1,num do
        local robotinfo = RobotMgr.handler.gen_robotinfo()
        table.insert(robot_infos,robotinfo)
    end
    return robot_infos
end

--保存机器人info信息
--机器人离开游戏后，需要将robotinfo放回robotpool
--robot_infos 机器人信息数组
function RobotMgr.CMD.save_robotinfo(robot_infos)
    if not RobotMgr.robotpool 
    or not RobotMgr.is_saverobot then
        filelog.sys_error("-----not save robot_infos-----")
        return
    end

    robot_infos = robot_infos or {}
    for _,robotinfo in pairs(robot_infos) do
        table.insert(RobotMgr.robotpool,robotinfo)
    end
    return true
end

--拒绝保存游戏服发来的机器人
--state true不保存机器人，false保存机器人
function RobotMgr.CMD.refuse_save_robotinfo(state)
    RobotMgr.is_saverobot = (not state)
end

-------------

skynet.start(function()
    skynet.dispatch("lua",function(session, source, cmd, ...)
        local f = RobotMgr.CMD[cmd]
        if f then
            skynet.retpack(f(...))
        else
            skynet.retpack()
        end
    end)
end)
